//
//  CLOCameraMainVC.swift
//  CLOCamera_Example
//
//  Created by Clover on 2018/5/17.
//  Copyright © 2018年 CocoaPods. All rights reserved.
//

import UIKit
import AVFoundation
import AssetsLibrary

@objc public protocol CLOCameraDelegate {
    /// 视频录制完成
    ///
    /// - Parameters:
    ///   - tempPath: 临时存储路径 为沙盒tmp文件夹下 名称为时间戳
    ///   - filePath: 相册中存储路径
    ///   - coverImage: 缩略图
    func cameraDidFinishRecordVideo(tempPath: String ,filePath: String, coverImage: UIImage)
    
    /// 图片完成
    ///
    /// - Parameters:
    ///   - image: 图片image信息
    ///   - imagePath: 相册中图片路径
    func cameraDidFinishRecordImage(image: UIImage, imagePath: String)
}

public class CLOCameraMainVC: UIViewController {
    public weak var delegate: CLOCameraDelegate?
    
    public let bundle: Bundle = Bundle.init(for: CLOCameraMainVC.self)
    /// 最大录制时间 默认20'
    public var MAXSeconds: Int = 20
    var seconds: Int = 0
    /// 转码清晰度参数 默认无损
    public var AVAssetExportPresetType: String = AVAssetExportPresetHighestQuality
    /// 摄像头
    private var captureDevice: AVCaptureDevice?
    /// 摄像头input
    private var captureDeviceInput: AVCaptureDeviceInput?
    /// 音频设备
    private var captureAudioDevice: AVCaptureDevice?
    /// 音频input
    private var captureAudioDeviceInput: AVCaptureDeviceInput?
    /// 视频输出流数据 --> Image为: AVCaptureStillImageOutput
    private var captureMovieFileOutput: AVCaptureMovieFileOutput = AVCaptureMovieFileOutput.init()
    /// 输入和输出设备之间的数据传递
    private var session: AVCaptureSession = AVCaptureSession.init()
    /// 图像预览layer
    private var previewLayer: AVCaptureVideoPreviewLayer?
    /// 是否摄像  默认false
    /// - true -> 录像  false -> 照片
    private var isVideo: Bool = false
    /// 后台任务标示
    private var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
    private var lastBackgroundTaskIdentifier: UIBackgroundTaskIdentifier?
    private var saveVideoUrl: URL?
    var play: CLOPlayer?
    // ----------------------- UI -----------------------//
    /// 权限认证view 最顶层
    @IBOutlet weak var authStatusLbl: UILabel!
    @IBOutlet weak var authView: UIView!
    /// 录像image
    @IBOutlet weak var imgRecord: UIImageView!
    /// 光标
    @IBOutlet weak var focusCursor: UIImageView!
    /// 主背景
    @IBOutlet var BG_IMG: UIImageView!
    /// 返回
    @IBOutlet var backBtn: UIButton!
    /// 切换
    @IBOutlet var cameraBtn: UIButton!
   /// 取消
    @IBOutlet weak var cancelBtn: UIButton!
    /// 确定
    @IBOutlet var ensureBtn: UIButton!
    /// 录像进度条
    @IBOutlet weak var progressView: CLOProgressView!
    /// 是否对焦中
    var isFocus: Bool = false
    /// 截屏image
    var takeImage: UIImage?
    /// 截屏view
    var takeImageView: UIImageView?
    
    //MARK: - life
    static public func viewFromNib() -> CLOCameraMainVC {
        let currentBundle = Bundle(for: CLOCameraMainVC.self)
        return currentBundle.loadNibNamed("CLOCameraMianVC", owner: nil, options: nil)?.last as! CLOCameraMainVC
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    override public func viewDidLoad() {
        super.viewDidLoad()
       
        getCarmeraAuth { (isAuth) in
            DispatchQueue.main.async {
                if isAuth{
                    self.authView.isHidden = true
                    self.authView.removeFromSuperview()
                    self.initDefauteUILayout()
                    self.initCarmera()
                }else{
                    self.authView.isHidden = false
                }
            }
        }
    }
    
    override public func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        session.startRunning()
    }
    override public func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        session.stopRunning()
    }
    
    //MARK: - public methods
    /// 拍摄完成调用
    func changeLayout() {
        self.imgRecord.isHidden = true;
        self.cameraBtn.isHidden = true;
        self.cancelBtn.isHidden = false;
        self.ensureBtn.isHidden = false;
        self.backBtn.isHidden   = true;
        
        if (self.isVideo) {
            self.progressView.clearProgress()
        }
        UIView.animate(withDuration: 0.25) {
            self.view.layoutIfNeeded()
        }
        self.lastBackgroundTaskIdentifier = self.backgroundTaskIdentifier
        self.backgroundTaskIdentifier = UIBackgroundTaskInvalid
        self.session.stopRunning()
    }
    
    /// 设置光标位置
    func setFocusCursor(point: CGPoint) {
        if (!self.isFocus) {
            self.isFocus = true;
            self.focusCursor.center = point;
            self.focusCursor.transform = CGAffineTransform(scaleX: 1.25, y: 1.25);
            self.focusCursor.alpha = 1.0;
            UIView.animate(withDuration: 0.3, animations: {
                self.focusCursor.transform = .identity;
            }, completion: { _ in
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.init(uptimeNanoseconds: UInt64(0.4)), execute: {
                    self.focusCursor.alpha = 0;
                    self.isFocus = false;
                })
            })
        }
    }
    
    /// 移除本地tmp临时视频文件
    public func removeTempVideo(){
        if self.saveVideoUrl != nil {
            do{
                try FileManager.default.removeItem(at: (self.saveVideoUrl)!)
            }catch _{}
        }
    }
    
    //MARK: - private methods
    
    /// 获取权限
    func getCarmeraAuth(isAuth:@escaping ((_ isAuth: Bool) -> ())){
        if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) {
            let videoStatus = AVCaptureDevice.authorizationStatus(for: .video)
            let audioStatus = AVCaptureDevice.authorizationStatus(for: .audio)
            var videoAuth = false
            var audioAuth = false
            if videoStatus == .notDetermined {// 第一次进未确定
                AVCaptureDevice.requestAccess(for: .video, completionHandler: { (success: Bool) in
                    videoAuth = success
                    if videoAuth && audioAuth{
                        isAuth(true)
                    }else{
                        isAuth(false)
                    }
                })
            }else if videoStatus == .denied || videoStatus == .restricted { //未授权
                videoAuth = false
            }else{
               videoAuth = true
            }
            
            
            if audioStatus == .notDetermined {// 第一次进未确定
                AVCaptureDevice.requestAccess(for: .audio, completionHandler: { (success: Bool) in
                    audioAuth = success
                    if videoAuth && audioAuth{
                        isAuth(true)
                    }else{
                        isAuth(false)
                    }
                })
            }else if audioStatus == .denied || audioStatus == .restricted { //未授权
                audioAuth = false
            }else{
                audioAuth = true
            }
            
            if videoAuth && audioAuth{
                isAuth(true)
            }else{
                isAuth(false)
            }
        }else{
            print("当前设备不支持拍照")
        }
    }
    
    func goToCameraAuth() {
        let videoUrl = URL.init(string: UIApplicationOpenSettingsURLString)
        if videoUrl != nil {
            if UIApplication.shared.canOpenURL(videoUrl!){
                UIApplication.shared.openURL(videoUrl!)
            }
        }
    }
    
    /// 初始化UI资源文件
    private func initDefauteUILayout(){
        let recordImgPath = bundle.path(forResource: "camera_start@2x.png", ofType: nil, inDirectory: "CLOCamera.bundle") ?? ""
        let backImgPath = bundle.path(forResource: "camera_back@2x.png", ofType: nil, inDirectory: "CLOCamera.bundle") ?? ""
        let confirmPath = bundle.path(forResource: "camera_confirm@2x.png", ofType: nil, inDirectory: "CLOCamera.bundle") ?? ""
        let cancelPath = bundle.path(forResource: "camera_cancel@2x.png", ofType: nil, inDirectory: "CLOCamera.bundle") ?? ""
        let changePath = bundle.path(forResource: "camera_change@2x.png", ofType: nil, inDirectory: "CLOCamera.bundle") ?? ""
        let gropPath = bundle.path(forResource: "camera_Group@2x.png", ofType: nil, inDirectory: "CLOCamera.bundle") ?? ""
      
        ensureBtn.setImage(UIImage.init(contentsOfFile: confirmPath), for: .normal)
        backBtn.setImage(UIImage.init(contentsOfFile: backImgPath), for: .normal)
        cameraBtn.setImage(UIImage.init(contentsOfFile: changePath), for: .normal)
        cancelBtn.setImage(UIImage.init(contentsOfFile: cancelPath), for: .normal)
        imgRecord.image = UIImage.init(contentsOfFile: recordImgPath)
        focusCursor.image = UIImage.init(contentsOfFile: gropPath)
        
        focusCursor.center = self.view.center
        
        self.progressView.isHidden = true
        self.progressView.layer.cornerRadius = self.progressView.frame.size.width/2;
    }
    
    private func initCarmera() {
        if session.canSetSessionPreset(.high) {
            session.sessionPreset = .high
        }
        captureDevice = getCameraDevice(position: .back)
        captureAudioDevice = AVCaptureDevice.devices(for: .audio).first
        
        do{
            try captureDeviceInput = AVCaptureDeviceInput.init(device: captureDevice!)
        }catch _{
//            print("取得“视频”设备输入对象时出错")
        }
        do{
            try captureAudioDeviceInput = AVCaptureDeviceInput.init(device: captureAudioDevice!)
        }catch _{
//            print("取得“音频”设备输入对象时出错")
        }

        /// 将输入设备添加到会话中
        if self.session.canAddInput(captureDeviceInput!){
            self.session.addInput(captureDeviceInput!)
            self.session.addInput(captureAudioDeviceInput!)
            // 设置防抖
            let connection: AVCaptureConnection? = captureMovieFileOutput.connection(with: .video)
            if let _ = connection?.isVideoStabilizationSupported{
                connection?.preferredVideoStabilizationMode = .cinematic
            }
        }
        
        /// 将输出设备添加到会话
        if self.session.canAddOutput(captureMovieFileOutput){
            self.session.addOutput(captureMovieFileOutput)
        }
        
        /// 创建视频预览层
        previewLayer = AVCaptureVideoPreviewLayer.init(session: session)
        previewLayer?.frame = self.view.frame
        // 填充模式
        previewLayer?.videoGravity = .resizeAspectFill
        BG_IMG.layer.addSublayer(previewLayer!)
//        addNotification(captureDevice: captureDevice!)
        addGenstureRecognizer()
    }
    
    /// 获取摄像设备
    private func getCameraDevice(position: AVCaptureDevice.Position) -> AVCaptureDevice?{
        let cameras = AVCaptureDevice.devices(for: .video)
        for camera in cameras {
            if camera.position == position
            {
                return camera
            }
        }
        return nil
    }
    
    /// 停止录制
    @objc  private func endRecord() {
        captureMovieFileOutput.stopRecording()
    }
    
   
    //MARK: - NSNotification
    /// 输入设备添加通知
    func addNotification(captureDevice: AVCaptureDevice){
        changeDeviceProperty { (device) in
            device.isSubjectAreaChangeMonitoringEnabled = true
        }
        let center =  NotificationCenter.default
        center.addObserver(self, selector: #selector(areaChange(noti:)), name: NSNotification.Name.AVCaptureDeviceSubjectAreaDidChange, object: captureDevice)
    }
    
    @objc private func areaChange(noti: NSNotification){
        
    }

}

//MARK: - Action
extension CLOCameraMainVC{
    //MARK: -| auth
    @IBAction func authClick(_ sender: UIButton){
        goToCameraAuth()
    }
    
    //MARK: -| Touch && Gesture
    public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = (touches as NSSet).anyObject() as! UITouch
        if (touch.view == imgRecord){
            print("点击录制按钮")
            seconds = MAXSeconds
            // 取得输入链接
            let connection = captureMovieFileOutput.connection(with: .audio)
            // 根据链接取得输出数据
            if !captureMovieFileOutput.isRecording {
                // 是否支持多任务
                if UIDevice.current.isMultitaskingSupported{
                    backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
                }
                if (saveVideoUrl != nil){
                    do{
                        try FileManager.default.removeItem(at: saveVideoUrl!)
                    }catch _ {}
                }
                connection?.videoOrientation = (previewLayer?.connection?.videoOrientation)!
                let outputFilePath: String = NSTemporaryDirectory().appending("movie.mov")
//                print("\(outputFilePath)")
                let fileUrl = URL.init(fileURLWithPath: outputFilePath)
                captureMovieFileOutput.startRecording(to: fileUrl, recordingDelegate: self)
            }else{
                captureMovieFileOutput.stopRecording()
            }
        }
        
    }
    
    public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = (touches as NSSet).anyObject() as! UITouch
        if (touch.view == imgRecord){
            if !isVideo{
                self.perform(#selector(endRecord), with: nil, afterDelay: 0.3)
            }else{
                endRecord()
            }
        }
    }
    
    func addGenstureRecognizer() {
        let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(tapScreen(tapGesture:)))
        self.BG_IMG.addGestureRecognizer(tapGesture)
    }
    
    @objc func tapScreen(tapGesture: UITapGestureRecognizer) {
        if session.isRunning {
            let point = tapGesture.location(in: BG_IMG)
            setFocusCursor(point: point)
            // 触摸屏幕的坐标点需要转换成0-1，设置聚焦点
            let cameraPoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
            focusWithMode(focusMode: AVCaptureDevice.FocusMode.autoFocus, exposureMode: AVCaptureDevice.ExposureMode.autoExpose, point: cameraPoint!)
        }
    }
    
    //MARK: -| Button
    /// 拍照 --> 确认
    @IBAction func sureCamera(_ sender: UIButton) {
        let assetsLibrary = ALAssetsLibrary.init()
        if saveVideoUrl != nil {
            assetsLibrary.writeVideoAtPath(toSavedPhotosAlbum: self.saveVideoUrl, completionBlock: { (url, _) in
                if self.lastBackgroundTaskIdentifier != UIBackgroundTaskInvalid{
                    UIApplication.shared.endBackgroundTask((self.lastBackgroundTaskIdentifier)!)
                }
                let image = self.videoHandlePhoto(url: self.saveVideoUrl!)
                self.changeVideoToMP4(movUrl: (self.saveVideoUrl)!, successBlock: { (mp4Path: String) in
                    if self.delegate != nil{
                        self.delegate?.cameraDidFinishRecordVideo(tempPath: mp4Path, filePath: (url?.absoluteString)!, coverImage: image)
                    }
                })
                
            })
        }else{
            if takeImage != nil{
                assetsLibrary.writeImage(toSavedPhotosAlbum: takeImage?.cgImage, metadata: nil, completionBlock: { (url, err) in
                    if err == nil{
                        if self.delegate != nil{
                            self.delegate?.cameraDidFinishRecordImage(image: (self.takeImage)!, imagePath: (url?.absoluteString)!)
                        }
                    }else{
                        print(err?.localizedDescription as Any)
                    }
                    
                })
            }
        }
        goBack(sender:UIButton.init())
    }
    
    /// 切换摄像头
    @IBAction func changeCamera(sender: UIButton) {
        let currentDevice = captureDeviceInput?.device
        let currentPosition = currentDevice?.position
        let toChangeDevice: AVCaptureDevice?
        // 前置
        var toChangePosition: AVCaptureDevice.Position = .front
        if currentPosition == .unspecified || currentPosition == .front{
            toChangePosition = .back
        }
        toChangeDevice = getCameraDevice(position: toChangePosition)
        // 生成新的input对象
        let toChangeDeviceInput = try! AVCaptureDeviceInput.init(device: toChangeDevice!)
        /// 更新配置
        session.beginConfiguration()
        // 移除old input
        session.removeInput(captureDeviceInput!)
        if session.canAddInput(toChangeDeviceInput){
            session.addInput(toChangeDeviceInput)
            captureDeviceInput = toChangeDeviceInput
        }
        session.commitConfiguration()
    }
    
    /// 返回
    @IBAction func goBack(sender: UIButton) {
        if (self.play != nil){
            self.play = nil
        }
        self.dismiss(animated: true, completion: nil)
    }
    
    /// 取消--> 重拍
    @IBAction func cancelClick(_ sender: UIButton) {
        if self.isVideo {
            self.isVideo = false
            self.play?.stopPlayer()
            self.play?.isHidden = true
        }
        self.session.startRunning()
        if takeImageView?.isHidden == false {
            takeImageView?.isHidden = true
        }
        
        self.imgRecord.isHidden = false;
        self.cameraBtn.isHidden = false;
        self.cancelBtn.isHidden = true;
        self.ensureBtn.isHidden = true;
        self.backBtn.isHidden   = false;
        self.view.layoutIfNeeded()
    }
}

//MARK: - AVCaptureFileOutputRecordingDelegate
extension CLOCameraMainVC: AVCaptureFileOutputRecordingDelegate{
    public func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
        print("视频开始录制")
        self.perform(#selector(onStartTranscribe(fileUrl:)), with: fileURL, afterDelay: 1.0)
    }
    
    public func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
        print("视频录制完成")
        changeLayout()
        if isVideo {
           saveVideoUrl = outputFileURL
            if play == nil {
                play = CLOPlayer.init(frame: self.BG_IMG.bounds, bgView: self.BG_IMG, url: outputFileURL)
                play?.starPlay()
            }else{
                play?.videoUrl = outputFileURL
                play?.isHidden = false
                play?.starPlay()
            }
        }else{// 图片
            saveVideoUrl = nil
            let image = videoHandlePhoto(url: outputFileURL)
            self.takeImage = image
            if takeImageView == nil {
                takeImageView = UIImageView.init(frame: self.view.frame)
                self.BG_IMG.addSubview(takeImageView!)
            }
            self.takeImageView?.isHidden = false
            self.takeImageView?.image = self.takeImage
        }
    }
    
    @objc func onStartTranscribe(fileUrl:URL) {
        if captureMovieFileOutput.isRecording {
            seconds -= 1
            if seconds > 0{
                if MAXSeconds - seconds >= 1 && !isVideo{// 视频
                    isVideo = true
                    progressView.maxTime = seconds
                    self.perform(#selector(onStartTranscribe(fileUrl:)), with: fileUrl, afterDelay: 1.0)
                }
            }else{
                if captureMovieFileOutput.isRecording {
                    captureMovieFileOutput.stopRecording()
                }
            }
        }
    }
    
    func videoHandlePhoto(url: URL) -> UIImage {
        let urlSet = AVURLAsset.init(url: url)
        let imageGenerator = AVAssetImageGenerator.init(asset: urlSet)
//        截图的时候调整到正确的方向
        imageGenerator.appliesPreferredTrackTransform = true
//        缩略图创建时间 CMTime是表示电影时间信息的结构体，第一个参数表示是视频第几秒，第二个参数表示每秒帧数.(如果要获取某一秒的第几帧可以使用CMTimeMake方法)
        let time = CMTime.init(value: 0, timescale: 30)
//        缩略图实际生成的时间
        var actucalTime: CMTime = CMTimeMake(0,0)
        
        let imageRef: CGImage = try! imageGenerator.copyCGImage(at: time, actualTime: &actucalTime)
        let image = UIImage.init(cgImage: imageRef)
        return image
    }
}


// MARK: - 配置环境参数
extension CLOCameraMainVC{
    func changeDeviceProperty(propertyChange: ((_ captureDevice: AVCaptureDevice) -> ())){
        let nowCaptureDevice = captureDeviceInput?.device
        do{
            try nowCaptureDevice?.lockForConfiguration()
//            if (nowCaptureDevice?.isWhiteBalanceModeSupported(AVCaptureDevice.WhiteBalanceMode.autoWhiteBalance))!{
//                nowCaptureDevice?.whiteBalanceMode = .autoWhiteBalance
//            }
//            if (nowCaptureDevice?.isFlashModeSupported(.auto))!{
//                nowCaptureDevice?.flashMode = .auto
//            }
            session.beginConfiguration()
            propertyChange(nowCaptureDevice!)
            nowCaptureDevice?.unlockForConfiguration()
            session.commitConfiguration()
        }catch{
           
        }
    }
    
    func focusWithMode(focusMode: AVCaptureDevice.FocusMode,exposureMode: AVCaptureDevice.ExposureMode, point: CGPoint) {
        changeDeviceProperty { (device) in
            if device.isFocusModeSupported(focusMode){
                device.focusMode = focusMode
            }
            if device.isExposureModeSupported(exposureMode){
                device.exposureMode = exposureMode
            }
            if (device.isFocusPointOfInterestSupported){
                device.focusPointOfInterest = point
            }
            if (device.isExposurePointOfInterestSupported){
                device.exposurePointOfInterest = point
            }
            
        }
    }
    
    /// 将mov转为MP4
    ///
    /// - Parameters:
    ///   - movUrl: movurl
    ///   - successBlock: 成功后的block
    func changeVideoToMP4(movUrl: URL, successBlock:@escaping ((_ newPath: String) -> ())){
        let avAsset = AVURLAsset.init(url: movUrl, options: nil)
        let dateStr = String.init(format: "%0.f", NSDate().timeIntervalSince1970)
        let destinationPath = NSTemporaryDirectory() + dateStr + ".mp4"
        let newVideoPath: NSURL = NSURL(fileURLWithPath: destinationPath as String)
        let exporter = AVAssetExportSession(asset: avAsset,
                                            presetName:AVAssetExportPresetType)!
        exporter.outputURL = newVideoPath as URL
        exporter.outputFileType = AVFileType.mp4
        exporter.shouldOptimizeForNetworkUse = true
        exporter.exportAsynchronously(completionHandler: {
            print("NewPath：\(String(describing: newVideoPath.relativePath))")
            successBlock(newVideoPath.relativePath ?? "")
        })
    }
}


//无权限
//                let alertVC = UIAlertController.init(title: "无相机权限", message: "请在iPhone的\"设置-隐私-照片\"选项中允许访问您的相机", preferredStyle: .alert)
//                let cancelAction = UIAlertAction.init(title: "取消", style: .cancel, handler: { (_) in
//                })
//
//                let sureAction = UIAlertAction.init(title: "确定", style: .`default`, handler: { (_) in
//                    self.goToCameraAuth()
//                })

//                alertVC.addAction(cancelAction)
//                alertVC.addAction(sureAction)
//                let rootVC = UIApplication.shared.keyWindow?.rootViewController
//                rootVC?.present(alertVC, animated: true, completion: nil)
